#include <gmpxx.h>
#include <fstream>
#include "Factor.hpp"
#include "Paire.hpp"
#include "Erathostene.hpp"
#include "Polynome.hpp"


Factor::Factor( int inNbPaire)
{
   _tabPaire = new Paire[ _nbPaire = inNbPaire ];
   _tailleRelle = 0;
}

Factor::~Factor()
{
  delete [] _tabPaire;
}

Factor::Factor(char * inChaine)
{
   
}


void Factor::rationnalBase(mpz_class ** inNbPremier, mpz_class inM )
{
   mpz_class tmp;
   
   for(int i = 0 ; i < _nbPaire && (mpz_cmp(((*inNbPremier)[i+1]).get_mpz_t(), inM.get_mpz_t()) < 0 ) ; i++)
   {
      
      ++_tailleRelle;
      mpz_mod(tmp.get_mpz_t(), inM.get_mpz_t(), (*inNbPremier)[i+1].get_mpz_t());
      _tabPaire[i].setPaire((*inNbPremier)[i+1], tmp);
   }
   //La taille reelle vient du fait que l'on ne connait pas le nombre de nombre permier
   //inferieur a m.On redéfinit donc la vrai taille de la base.
   _nbPaire = _tailleRelle;
}


int Factor::algebraicBase(mpz_class ** inNbPremier, int indiceMax, Polynome & inPoly)
{
   mpz_class r;
   mpz_class nombre;
   mpz_class reste;
   mpz_class tmp;
   int indicePaire = 0;
   int i = 0;
   //On remplit le bon nombre de paire en parcourant notre tableau de nombre premier

   for( ; i < indiceMax && indicePaire < _nbPaire ; i++)
   {
      nombre = (*inNbPremier)[i+1];    //On enleve le premier couple
      for(r = 0 ; r < nombre && indicePaire < _nbPaire ; r += 1)
      {
         tmp = inPoly.eval(r)%nombre;
         if( ! mpz_cmp_ui(tmp.get_mpz_t(), 0) )
         {
            //On a une racine donc on insere le couple
            _tabPaire[indicePaire++].setPaire(nombre,r);
            _tailleRelle++;
         }
      }
      
   }
   //On retourne  le prochain element
   _nbPaire = _tailleRelle;
   return i+1;
}


void Factor::quadraticCharacterBase(mpz_class ** inNbPremier, int indiceDebut, int indiceMax, Polynome & inPoly)
{

   
   // On test si on ne deborde pas
   if( indiceDebut + _nbPaire < indiceMax )
   {
      mpz_class r;
      mpz_class nombre;
      mpz_class reste;
      mpz_class tmp;
      int indicePaire = 0;
      
      //On remplit le bon nombre de paire en parcourais notre tableau de nombre premier

      for(int i = indiceDebut ; i < indiceMax && indicePaire < _nbPaire ; i++)
      {
         nombre = (*inNbPremier)[i];
         for(r = 0 ; r < nombre && indicePaire < _nbPaire; r += 1)
         {
            tmp = inPoly.eval(r)%nombre;
            if( ! mpz_cmp_ui(tmp.get_mpz_t(), 0) )
            {
               //On a une racine donc on insere le couple
               _tabPaire[indicePaire++].setPaire(nombre,r);
               _tailleRelle++;
            }
         }
         
      }
   }
   
   _nbPaire = _tailleRelle;

   
   
}


bool Factor::add(Paire & inPaire)
{
   if(_tailleRelle < _nbPaire)
   {
      _tabPaire[_tailleRelle++] = inPaire;
      return true;
   }
   
   return false;
}

ostream & operator << ( ostream & o,const Factor & inFactor)
{
   int nbPaire = inFactor.getPaire();   
   for(int i = 0 ; i < nbPaire && !(inFactor._tabPaire[i].isNull()) ; i++)
   {
      o << inFactor._tabPaire[i];
   }
   
   return  o << endl;
}

//Exemple : 2@1|3@1|5@1|7@3|11@9|13@5|17@14|19@12|23@8|29@2|
char * Factor::converMPI() const
{
   char * res = new char[tailleMax];
   char * tmp;
   res[0] = '\0';
   if(_tailleRelle)
   {
      int j = 0;
      
      int inserSepa;
      
   
      //On prend une marge de 100 chifres par element de la paire
      //pour ne pas depasser
      for( ; j < _nbPaire && strlen(res) < tailleMax - 200  ; j++)
      {
         tmp =  _tabPaire[j].converMPI();
         strcat(res, tmp);
         inserSepa = strlen(res);
         res[inserSepa] = '|';
         res[inserSepa + 1] = '\0';
         delete [] tmp;
      }
   }

   return res;
}
   
   
bool Factor::converFactor(char * inChaine)
{
   purgeBase();
   if(inChaine)
   {
      int i = 0;
      int indiceFactor = 0;
      int tailleChaine = 0;
      char * tmp = new char[100];
      char * ptrDebut = &inChaine[0];
      
      mpz_class nb = 1;
      Paire paireTmp(nb, nb);
      
      while( !_tailleRelle || ! paireTmp.isNull())
      {
         if(inChaine[i] == '|')
         {
            if(indiceFactor)
            {
               add(paireTmp);
            }
            
            memset(tmp, '\0', 100 * sizeof(char));
            strncpy(tmp, ptrDebut, tailleChaine);
            
            paireTmp.converPaire(tmp);
                        
            tailleChaine = 0;
            
            indiceFactor++;
            
            if(inChaine[i+1])
            {
               ptrDebut = &inChaine[i+1];
            }
         }
         i++;
         tailleChaine++;

      }
      delete [] tmp;
      return true;
   }
   else
   {
      return false;
   }
}

void Factor::generateFile(Factor & AFB, Factor & RFB, Factor & QCB, Factor & AB)
{
   ofstream fAFB;
   ofstream fRFB;
   ofstream fQCB;
   ofstream fAB;
   ofstream fAB2;
   
   fAFB.open("notregnfs.afb", ios::out);
   fRFB.open("notregnfs.rfb", ios::out);
   fQCB.open("notregnfs.qcb", ios::out);
   fAB.open("notregnfs.rel", ios::out);
   fAB2.open("notregnfs.line", ios::out);
   
  
   

   if(fAFB.bad() || fRFB.bad() || fQCB.bad() || fAB.bad() || fAB2.bad())
   {
      exit(-1);
   }
   
   // Remplissage des paires de AFB
   for(int i = 0 ; i < AFB.getTaille() ; i++)
   {
      fAFB << AFB.getPaire(i).getPaire(0) << endl;
      fAFB << AFB.getPaire(i).getPaire(1) << endl;
   }
   
   fAFB.close();

   // Remplissage des paires de RFB
   for(int i = 0 ; i < RFB.getTaille() ; i++)
   {
      fRFB << RFB.getPaire(i).getPaire(0) << endl;
      fRFB << RFB.getPaire(i).getPaire(1) << endl;
   }
   
   fRFB.close();
   

   
      // Remplissage des paires de QCB
   for(int i = 0 ; i < QCB.getTaille() ; i++)
   {
      fQCB << QCB.getPaire(i).getPaire(0) << endl;
      fQCB << QCB.getPaire(i).getPaire(1) << endl;
   }
   
   fQCB.close();
   

   
   // Remplissage des paires de AB
   for(int i = 0 ; i < AB.getTaille() ; i++)
   {
      fAB << AB.getPaire(i).getPaire(0) << endl;
      fAB << AB.getPaire(i).getPaire(1) << endl;
   }
   

   fAB.close();
   
   // Remplissage du fichier annexe pour les paires (a, b)
   // Valeur du plus grand b utilise :
   fAB2 << AB.getPaire(AB.getTaille() - 1).getPaire(1) << endl;
   // Nombre de paires dans AB
   fAB2 << AB.getTaille() << endl;
   fAB2.close();
}
